package com.idol.framework.handler;

import com.idol.framework.annotation.Controller;
import com.idol.framework.annotation.RequestMapping;
import com.idol.framework.annotation.Security;
import com.idol.framework.factory.BeanContainer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Supreme_Sir
 * @version 1.0
 * @className HandlerMapping
 * @description 处理器映射器
 * @date 2020/10/31 20:19
 **/
public class HandlerMapping {
    private static final HandlerMapping INSTANCE = new HandlerMapping();
    private BeanContainer beanContainer = BeanContainer.getInstance();
    private List<Handler> handlerContainer = new ArrayList<>();

    /**
     * 获取与请求对应的处理器
     * @param request
     * @return
     */
    public Handler getMatchedHandler(HttpServletRequest request) {
        if (handlerContainer.isEmpty()) {
            return null;
        }
        String url = request.getRequestURI();

        for (Handler handler : handlerContainer) {
            Matcher matcher = handler.getPattern().matcher(url);
            if (!matcher.matches()) {
                continue;
            }
            return handler;
        }
        return null;
    }

    /**
     * 初始化处理器映射器
     * @return
     */
    public void initHandlerMapping() {
        Set<Map.Entry<String, Object>> entrySet = beanContainer.getEntrySet();
        if (entrySet.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : entrySet) {
            Class<?> beanClass = entry.getValue().getClass();
            if (!beanClass.isAnnotationPresent(Controller.class)) {
                continue;
            }

            // 获取在类上添加的 Security 注解的值
            List<String> typePermission = new ArrayList<>();
            if (beanClass.isAnnotationPresent(Security.class)) {
                String[] userArr = beanClass.getAnnotation(Security.class).value();
                typePermission.addAll(Arrays.asList(userArr));
            }

            // 获取在类上添加的 RequestMapping 注解的值
            String baseUrl = "";
            if (beanClass.isAnnotationPresent(RequestMapping.class)) {
                baseUrl = beanClass.getAnnotation(RequestMapping.class).value();
            }

            // 获取在方法上添加的 Security 和 RequestMapping 注解的值
            Method[] declaredMethods = beanClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }

                // 获取在方法上添加的 Security 注解的值
                List<String> methodPremission = new ArrayList<>();
                // 将添加在类上的放行用户合并至方法放行用户名单
                methodPremission.addAll(typePermission);
                if (method.isAnnotationPresent(Security.class)) {
                    String[] userArr = method.getAnnotation(Security.class).value();
                    methodPremission.addAll(Arrays.asList(userArr));
                }

                RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                String methodUrl = annotation.value();
                String url = baseUrl + methodUrl;

                Handler handler = new Handler(entry.getValue(), method, Pattern.compile(url), methodPremission);
                Map<String, Integer> paramIndexMapping = new HashMap<>(16);
                handler.setParamIndexMapping(paramIndexMapping);
                Parameter[] parameters = method.getParameters();

                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    // 这里对 HttpServletRequest 和 HttpServletResponse 进行按类名存储，只为了方便后面方便取用
                    if (parameter.getType() == HttpServletRequest.class) {
                        paramIndexMapping.put("HttpServletRequest", i);
                        handler.setNeedRequest(true);
                    } else if (parameter.getType() == HttpServletResponse.class){
                        paramIndexMapping.put("HttpServletResponse", i);
                        handler.setNeedResponse(true);
                    }else {
                        paramIndexMapping.put(parameter.getName(), i);
                    }
                }

                handlerContainer.add(handler);
            }
        }
    }

    public static HandlerMapping getInstance() {
        return INSTANCE;
    }

    private HandlerMapping() {
    }
}
